/*
 * @ProjectName: 编程学习
 * @Copyright:   2019 HangZhou Yi Dev, Ltd. All Right Reserved.
 * @address:     https://yiyuery.github.io/
 * @date:        2019/5/20 20:57
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用，转发请注明出处.
 */
package com.example.common.util;

import com.example.common.exception.TransformException;
import org.apache.commons.lang3.StringUtils;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>
 *
 * </p>
 *
 * @author Yi
 * @version v1.0.0
 * @date 2019-07-06 23:01
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2019-07-06
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class CapDateTimeUtil {

    private CapDateTimeUtil() {
    }

    private static class SingleTon {
        private final static CapDateTimeUtil INSTANCE = new CapDateTimeUtil();
    }

    public static CapDateTimeUtil getInstance() {
        return SingleTon.INSTANCE;
    }

    /**
     * 获取时间实例
     *
     * @return
     */
    public static Date currentDate() {
        return new Date(currentTimeMillis());
    }

    public static class DateFormatConstant {
        public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
        public static final String MM_DD = "MM-dd";
        public static final String MMDD = "MMdd";
        public static final String MMDDHH = "MMddHH";
        public static final String YYMMDD = "yyMMdd";
        public static final String YYMM = "yyMM";
        public static final String HHMM = "HHmm";
        public static final String HH = "HH";
        public static final String YY_MM = "yy-MM";
        public static final String YYYY_MM_DD = "yyyy-MM-dd";
        public static final String DAY_END_MOMENT_TIME_STR_HH_MM_SS = " 23:59:59";
        public static final String DAY_START_MOMENT_TIME_STR_HH_MM_SS = " 00:00:00";
        public static final Integer HOUR_DAY_LENGTH = 24;
        public static final Integer WEEK_LENGTH = 7;
        public static final Integer MONTH_LENGTH = 30;
        public static final Integer MONTH_YEAR_LENGTH = 12;
        public static final Integer YEAR_LENGTH = 365;
        public static final String[] WEEK_DAY_ITEMS = new String[]{
                "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"
        };
        public static final String[] MONTH_DAY_ITEMS = new String[]{
                "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Sept", "Sept", "Dec"
        };
    }

    /**
     * 默认时间转换工具
     */
    private static SimpleDateFormat SDF_DEFAULT = new SimpleDateFormat(DateFormatConstant.YYYY_MM_DD_HH_MM_SS);

    /**
     * System millis provider.
     */
    static class SystemMillisProvider {
        /**
         * Gets the current time.
         *
         * @return the current time in millis
         */
        public static long getMillis() {
            return System.currentTimeMillis();
        }
    }

    /**
     * 获取当前时间
     *
     * @return
     */
    public static final long currentTimeMillis() {
        return SystemMillisProvider.getMillis();
    }

    /**
     * 时间格式化
     *
     * @param millis
     * @param fmt
     * @return
     */
    public String dateFormat(Long millis, String fmt) {
        try {
            SimpleDateFormat sdf = getSimpleDateFormatInstance(fmt);
            return sdf.format(new Date(millis));
        } catch (Throwable e) {
            throw new TransformException("date transform failed!", e);
        }
    }

    private SimpleDateFormat getSimpleDateFormatInstance(String fmt) {
        return new SimpleDateFormat(StringUtils.isNotBlank(fmt) ? fmt : DateFormatConstant.YYYY_MM_DD_HH_MM_SS);
    }

    public TimeHolder get24HourStart() {
        return new TimeHolder(getLastHoursToday(TimeBound.START, DateFormatConstant.HOUR_DAY_LENGTH));
    }

    /**
     * 获取上一周的今天的最后时刻
     *
     * @return
     */
    public TimeHolder getLastWeekTodayEnd() {
        return new TimeHolder(getLastPeriodToday(TimeBound.END, DateFormatConstant.WEEK_LENGTH));
    }

    /**
     * 获取上一周的今天的开始时刻
     *
     * @return
     */
    public TimeHolder getLastWeekTodayStart() {
        return new TimeHolder(getLastPeriodToday(TimeBound.START, DateFormatConstant.WEEK_LENGTH));
    }

    /**
     * 获取上一月的今天的最后时刻
     *
     * @return
     */
    public TimeHolder getLastMonthTodayEnd() {
        return new TimeHolder(getLastPeriodToday(TimeBound.END, DateFormatConstant.MONTH_LENGTH));
    }

    /**
     * 获取上一月的今天的开始时刻
     *
     * @return
     */
    public TimeHolder getLastMonthTodayStart() {
        return new TimeHolder(getLastPeriodToday(TimeBound.START, DateFormatConstant.MONTH_LENGTH));
    }

    /**
     * 获取上一月的今天的最后时刻
     *
     * @return
     */
    public TimeHolder getLastYearTodayEnd() {
        return new TimeHolder(getLastPeriodToday(TimeBound.END, DateFormatConstant.YEAR_LENGTH));
    }

    /**
     * 获取上一月的今天的开始时刻
     *
     * @return
     */
    public TimeHolder getLastYearTodayStart() {
        return new TimeHolder(getLastPeriodToday(TimeBound.START, DateFormatConstant.YEAR_LENGTH));
    }

    /**
     * 获取上一周时间
     *
     * @return
     */
    private String getLastPeriodToday(TimeBound bound, Integer beforeDays) {
        try {
            SimpleDateFormat sdf = getSimpleDateFormatInstance(DateFormatConstant.YYYY_MM_DD);
            Calendar calendar = new GregorianCalendar();
            calendar.setTime(currentDate());
            calendar.add(Calendar.DATE, -beforeDays);
            String currentPrefix = sdf.format(calendar.getTime());
            return jointWithBound(currentPrefix, bound);
        } catch (Throwable e) {
            throw new TransformException("date transform failed!", e);
        }
    }

    /**
     * 获取24小时前
     *
     * @return
     */
    private String getLastHoursToday(TimeBound bound, Integer before) {
        try {
            SimpleDateFormat sdf = getSimpleDateFormatInstance(DateFormatConstant.YYYY_MM_DD_HH_MM_SS);
            Calendar calendar = new GregorianCalendar();
            calendar.setTime(currentDate());
            calendar.add(Calendar.HOUR, -before);
            return sdf.format(calendar.getTime());
        } catch (Throwable e) {
            throw new TransformException("date transform failed!", e);
        }
    }


    /**
     * 获取今天开始时间
     *
     * @return
     */
    public TimeHolder getTodayStart() {
        return new TimeHolder(getTodayWithBound(TimeBound.START));
    }

    /**
     * 获取今天结束时间
     *
     * @return
     */
    public TimeHolder getTodayEnd() {
        return new TimeHolder(getTodayWithBound(TimeBound.END));
    }

    /**
     * 获取今天结束时间
     *
     * @return
     */
    private String getTodayWithBound(TimeBound bound) {
        try {
            SimpleDateFormat sdf = getSimpleDateFormatInstance(DateFormatConstant.YYYY_MM_DD);
            String currentPrefix = sdf.format(currentTimeMillis());
            return jointWithBound(currentPrefix, bound);
        } catch (Throwable e) {
            throw new TransformException("date transform failed!", e);
        }
    }

    /**
     * 根据边界拼接
     *
     * @param currentPrefix
     * @param bound
     * @return
     */
    private String jointWithBound(String currentPrefix, TimeBound bound) {
        String result = StringUtils.EMPTY;
        switch (bound) {
            case END:
                result = currentPrefix + DateFormatConstant.DAY_END_MOMENT_TIME_STR_HH_MM_SS;
                break;
            case START:
                result = currentPrefix + DateFormatConstant.DAY_START_MOMENT_TIME_STR_HH_MM_SS;
                break;
            default:
                break;
        }
        return result;
    }


    /**
     * 获取24小时内时间刻度
     * @return
     */
    public Collection<? extends String> getHourOfDayScale() {
        List<String> scales = new ArrayList<>();
        Calendar calendar = new GregorianCalendar();
        Date current = currentDate();
        SimpleDateFormat sdf = getSimpleDateFormatInstance(DateFormatConstant.MMDDHH);
        for (int i = DateFormatConstant.HOUR_DAY_LENGTH - 1; i >= 0; i--) {
            calendar.setTime(current);
            calendar.add(Calendar.HOUR, -i);
            scales.add(sdf.format(calendar.getTime()));
        }
        return scales;
    }


    /**
     * 返回一天24小时刻度
     *
     * @return
     */
    public List<String> getHourLabelsOfDayScale() {
        List<String> scales = new ArrayList<>();
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(currentDate());
        int index = calendar.get(Calendar.HOUR);
        int pm = calendar.get(Calendar.AM_PM);
        if (pm > 0) {
            index += 12;
        }
        String item = StringUtils.EMPTY;
        for (int i = index + 1; i < DateFormatConstant.HOUR_DAY_LENGTH; i++) {
            item = String.valueOf(i);
            scales.add(item.length() == 1 ? "0" + item : item);
        }
        for (int i = 0; i < index + 1; i++) {
            item = String.valueOf(i);
            scales.add(item.length() == 1 ? "0" + item : item);
        }
        return scales;
    }


    /**
     * 返回近一周的刻度
     *
     * @return
     */
    public List<String> getDayLabelsOfWeekScale() {
        List<String> scales = new ArrayList<>();
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(currentDate());
        int index = calendar.get(Calendar.DAY_OF_WEEK);
        scales.addAll(Arrays.asList(DateFormatConstant.WEEK_DAY_ITEMS).subList(index - 1, DateFormatConstant.WEEK_LENGTH));
        scales.addAll(Arrays.asList(DateFormatConstant.WEEK_DAY_ITEMS).subList(0, index - 1));
        return scales;
    }

    /**
     * 返回近一周的刻度
     *
     * @return
     */
    public List<String> getDateOfWeekScale() {
        List<String> scales = new ArrayList<>();
        Calendar calendar = new GregorianCalendar();
        Date current = currentDate();
        SimpleDateFormat sdf = getSimpleDateFormatInstance(DateFormatConstant.MMDD);
        for (int i = DateFormatConstant.WEEK_LENGTH - 1; i >= 0; i--) {
            calendar.setTime(current);
            calendar.add(Calendar.DATE, -i);
            scales.add(sdf.format(calendar.getTime()));
        }
        return scales;
    }

    /**
     * 返回近一月的刻度
     *
     * @return
     * @param mmDd
     */
    public List<String> getDayOfMonthScale(String fmt) {
        List<String> scales = new ArrayList<>();
        Calendar calendar = new GregorianCalendar();
        Date current = currentDate();
        SimpleDateFormat sdf = getSimpleDateFormatInstance(fmt);
        for (int i = DateFormatConstant.MONTH_LENGTH; i > -1; i--) {
            calendar.setTime(current);
            calendar.add(Calendar.DATE, -i);
            scales.add(sdf.format(calendar.getTime()));
        }
        return scales;
    }


    /**
     * 返回近一月的刻度
     *
     * @return
     * @param yyMm
     */
    public List<String> getMonthOfYearScale(String fmt) {
        List<String> scales = new ArrayList<>();
        Calendar calendar = new GregorianCalendar();
        Date current = currentDate();
        SimpleDateFormat sdf = getSimpleDateFormatInstance(fmt);
        for (int i = DateFormatConstant.MONTH_YEAR_LENGTH; i > -1; i--) {
            calendar.setTime(current);
            calendar.add(Calendar.MONTH, -i);
            scales.add(sdf.format(calendar.getTime()));
        }
        return scales;
    }

    /**
     * 时间边界
     */
    enum TimeBound {
        //开始、结束
        START, END
    }

    public class TimeHolder {
        private String timeStr;
        private Date date;

        public TimeHolder(String timeStr) {
            this.timeStr = timeStr;
        }

        public String getTimeStr() {
            return timeStr;
        }

        public Date getDate() {
            try {
                return getSimpleDateFormatInstance(null).parse(getTimeStr());
            } catch (Throwable e) {
                throw new TransformException("transform failed!", e);
            }
        }
    }

    public static void main(String[] args) {
        System.out.println(CapDateTimeUtil.getInstance().getMonthOfYearScale(DateFormatConstant.YY_MM));
        System.out.println(CapDateTimeUtil.getInstance().getHourOfDayScale());
        System.out.println(CapDateTimeUtil.getInstance().getDateOfWeekScale());
    }
}
